﻿using FAfx.SpectrumFish.Interfaces.Hardware;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Num = System.Int32;
using Word = System.Int32;

namespace FAfx.SpectrumFish.Hardware
{
    internal partial class Z80 : IZ80
    {

#region Registers & Stuff
       public IMemory memory { get; set; }
       public IPorts port { get; set; }

       public Num a { get; set; }
       public Num f { get; set; }
       public Num b { get; set; }
       public Num c { get; set; }
       public Num d { get; set; }
       public Num e { get; set; }
       public Num h { get; set; }
       public Num l { get; set; }

       public Num a_ { get; set; }
       public Num f_ { get; set; }
       public Num b_ { get; set; }
       public Num c_ { get; set; }
       public Num d_ { get; set; }
       public Num e_ { get; set; }
       public Num h_ { get; set; }
       public Num l_ { get; set; }

       public Num ixh { get; set; }
       public Num ixl { get; set; }
       public Num iyh { get; set; }
       public Num iyl{ get; set; }
       public Num sp { get; set; }        
       public Num pc { get; set; }

       public Num r { get; set; }
       public Num r7 { get; set; }

       public Num i { get; set; }
       public Num iff1 { get; set; }
       public Num iff2 { get; set; }
       public Num im { get; set; }

       public bool halted { get; set; }

       public Num tstates { get; set; }
       public Num event_next_event { get; set; }
        
#endregion

        //-----------------------------------------------------------------------;
        //cOpcodes _Opcodes = null; 
        Dictionary<Num, Action> interuptLU = new Dictionary<Num, Action>();

        private Dictionary<Num, Action> _Opcodes;

        internal Z80(IMemory aMemory, IPorts aPort)
        {
            memory = aMemory;
            port = aPort;
            InitOpcodes();

            interuptLU.Add(0, (() =>
            {
                this.pc = 0x0038;
                this.tstates += 12;
            }));

            interuptLU.Add(1, (() =>
            {
                this.pc = 0x0038;
                this.tstates += 13;
            }));

            interuptLU.Add(2, (() =>
            {
                var inttemp = (0x100 * this.i) + 0xff;
                var pcl = this.memory.readbyte(inttemp++);
                inttemp &= 0xfff;
                var pch = this.memory.readbyte(inttemp);
                this.pc = pcl | (pch << 8);
                this.tstates += 19;
            }));
        }

        //-----------------------------------------------------------------------;
        //http://msdn.microsoft.com/en-us/library/336aedhh(VS.80).aspx
        public override bool Equals(Object obj)
        {            
            bool ret = false;

            // Check for null values and compare run-time types.
            if (obj == null || GetType() != obj.GetType())
                return ret;

            Z80 t = (Z80)obj;

            ret = (a == t.a) && (f == t.f) && (b == t.b) && (c == t.c) && (d == t.d) && (e == t.e) && (h == t.h) && (l == t.l);
            ret &= (a_ == t.a_) && (f_ == t.f_) && (b_ == t.b_) && (c_ == t.c_) && (d_ == t.d_) && (e_ == t.e_) && (h_ == t.h_) && (l_ == t.l_);

            ret &= (ixh == t.ixh) && (ixl == t.ixl) && (iyh == t.iyh) && (iyl == t.iyl);
            ret &= (i == t.i) && (r == t.r) && (r7 == t.r7);

            ret &= (sp == t.sp) && (pc == t.pc) && (iff1 == t.iff1) && (iff2 == t.iff2) && (im == t.im);

            ret &= (halted == t.halted);
            ret &= (tstates == t.tstates);

            ret &= (memory == t.memory);
            ret &= (port == t.port);
            ret &= (event_next_event == t.event_next_event);

            return ret;
        }

        internal void do_opcodes()
        {
            while (this.tstates < this.event_next_event)
            {
                this.tstates += 4;
                this.r = (this.r + 1) & 0x7f;
                Num opcodecc = this.memory.readbyte(this.pc++);
                this.pc &= 0xffff;
                _Opcodes[opcodecc].Invoke();
            }
        }

        internal void reset()
        {
            this.a = this.f = this.b = this.c = this.d = this.e = this.h = this.l = 0;
            this.a_ = this.f_ = this.b_ = this.c_ = this.d_ = this.e_ = this.h_ = this.l_ = 0;
            this.ixh = this.ixl = this.iyh = this.iyl = 0;
            this.i = this.r = this.r7 = 0;
            this.sp = this.pc = 0;
            this.iff1 = this.iff2 = this.im = 0;
            this.halted = false;
            this.tstates = 0;
        }
        internal void interrupt()
        {
            if (this.iff1 > 0)
            {
                if (this.halted)
                {
                    this.pc++;
                    this.pc &= 0xffff;
                    this.halted = false;
                }
                this.iff1 = this.iff2 = 0;
                this.sp = (this.sp - 1) & 0xffff;
                this.memory.writebyte(this.sp, (this.pc >> 8));
                this.sp = (this.sp - 1) & 0xffff;
                this.memory.writebyte(this.sp, (this.pc & 0xff));
                this.r = (this.r + 1) & 0x7f;
                interuptLU[this.im].Invoke();
            }
        }

        internal void nmi()
        {
            this.iff1 = 0;
            this.sp = (this.sp - 1) & 0xffff;
            this.memory.writebyte(this.sp, (this.pc >> 8));
            this.sp = (this.sp - 1) & 0xffff;
            this.memory.writebyte(this.sp, (this.pc & 0xff));
            this.tstates += 11;
            this.pc = 0x0066;
        }


    }
}
